package dungeon;

import java.util.HashMap;
import java.util.HashSet;
import java.util.NoSuchElementException;
import java.util.Set;

import java.util.Iterator;

import condition.Condition;

import be.kuleuven.cs.som.annotate.Basic;
import be.kuleuven.cs.som.annotate.Model;
import be.kuleuven.cs.som.annotate.Raw;
import border.Door;

import square.Direction;
import square.Rock;
import square.Square;
import square.TeleportationSquare;
import dungeon.Position;

public class ComposedDungeon<E extends Square> extends Dungeon<E> {
	
	/**
	 * Initializing this new composed dungeon with
	 * no sub dungeons attached to it and with
	 * the given coordinates as maximal coordinates
	 * for this composed dungeon.
	 * 
	 * @param 	maxX
	 * 			The maximum value for the x coordinate of a position in a composed dungeon.
	 * @param 	maxY
	 * 			The maximum value for the Y coordinate of a position in a composed dungeon.
	 * @param 	maxZ
	 * 			The maximum value for the Z coordinate of a position in a composed dungeon.
	 * @post	The maximal values for coordinates of a position in a composed dungeon
	 * 			are set to the given values.
	 * 			| getMaxX() == maxX && getMaxY() == maxY && getMaxZ() == maxZ
	 * @post	No sub dungeons are attached to this new composed dungeon.
	 * 			|new.getNbSubDungeons() == 0
	 */
	@Raw
	public ComposedDungeon(long maxX, long maxY, long maxZ){
		super(maxX,maxY,maxZ);
	}
	
	/**
	 * Terminate this composed dungeon.
	 * 
	 * @post 	If this dungeon has a containing dungeon,
	 * 			that composed dungeon no longer references 
	 * 			this dungeon as one of its sub dungeons.
	 * 			|if(getContainingDungeon() != null)
	 * 			|	then ! (new this.getContainingDungeon()).hasAsSubDungeon(this)
	 * @post	The containing dungeon of this dungeon
	 * 			references null.
	 * 			| getContainingDungeon() == null
	 * @effect	Each sub dungeon associated with this 
	 * 			composed dungeon is removed as a sub dungeon.
	 * 			|for each dungeon in getSubDungeons()
	 * 			|	this.removeSubDungeon(dungeon)
	 */
	@Override
	public void terminate() {
		this.isTerminated = true;
		getContainingDungeon().removeAsSubDungeon(this);
		setContainingDungeon(null);
		for(Dungeon<E> subDungeon: getAllSubDungeons())
			subDungeon.terminate();
		subDungeons.clear();
	}
	
	/**
	 * Return an iterator returning squares in this composed
	 * dungeon.
	 */
	public Iterator<E> getSquares(final Condition condition){
		return new Iterator<E>(){
			
			{
				IteratorOverSubDungeons = subDungeons.keySet().iterator();
				if(subDungeons.keySet().iterator().hasNext())
					subDungeonIterator = subDungeons.keySet().iterator().next().getSquares(condition);
			}


			@Override
			public boolean hasNext() {
				if(subDungeonIterator.hasNext())
					return true;
				
				if(IteratorOverSubDungeons.hasNext()){
					subDungeonIterator = IteratorOverSubDungeons.next().getSquares(condition);
					return hasNext();
				}
				return false;
			}

			@Override
			public E next() {
				if(!hasNext())
					throw new NoSuchElementException();
				if(subDungeonIterator.hasNext())
					return subDungeonIterator.next();
				subDungeonIterator = IteratorOverSubDungeons.next().getSquares(condition);				return null;
			}
			
			@Override
			public void remove() throws UnsupportedOperationException{
				throw new UnsupportedOperationException();
			}
			
			private Iterator<E> subDungeonIterator;
			private Iterator<Dungeon<E>> IteratorOverSubDungeons;
			
		};
	}
	
	/**
	 * Return a list of all dungeons associated with this composed
	 * dungeon.
	 *
	 * @return 	The resulting set is effective.
	 * 			| result != null
	 * @return	The number of elements in the resulting set is equal to 
	 * 			the number of sub dungeons associated with this 
	 * 			composed dungeon.
	 * 			| result.size() == getNbSubDungeons()
	 * @return	Each element in the resulting set is a sub dungeon
	 * 			associated with this composed dungeon.
	 * 			| for each dungeon in result
	 * 			|	this.hasAsSubDungeon(dungeon)
	 */
	public Set<Dungeon<E>> getAllSubDungeons(){
		return subDungeons.keySet();
	}

	/**
	 * Return the sub dungeon associated with this composed
	 * dungeon at the given position.
	 * 
	 * @param	position
	 * 			The position of the dungeon to be returned.
	 * @return	The dungeon that is associated with this 
	 * 			composed dungeon at the given position.
	 * 			|result.positionLiesInDungeon(position.subtract(getPositionOfSubDungeon(result))
	 * 			|	== true
	 */
	@Basic	@Raw
	public Dungeon<E> getSubDungeonAt(Position position){
		if(! positionLiesInDungeon(position))
			return null;
		Dungeon<E> dungeon = null;
		if(getNbSubDungeons() != 0){
			Iterator<Dungeon<E>> it = subDungeons.keySet().iterator();
			while(dungeon == null && it.hasNext()){
				Dungeon<E> nextDungeon = it.next();
				try{
				if(nextDungeon.positionLiesInDungeon(position.subtract(subDungeons.get(nextDungeon))))
					dungeon = nextDungeon;}
				catch(IllegalArgumentException exc){}
			}
		}
		return dungeon;
	}

	/**
	 * Return the position at which the given dungeon is 
	 * registered as a sub dungeon for this composed dungeon.
	 * 
	 * @param 	dungeon
	 * 			The dungeon to look for.
	 * @throws	IllegalArgumentException
	 * 			The given dungeon is not a sub dungeon for this
	 * 			composed dungeon.
	 * 			| ! hasAsSubDungeon(dungeon)
	 */
	@Basic	@Raw
	public Position getPositionOfSubDungeon(Dungeon<E> dungeon){
		if(! hasAsSubDungeon(dungeon))
			throw new IllegalArgumentException("The given dungeon is not a sub dungeon for this composed dungeon");
		return subDungeons.get(dungeon);
	}
	
	/**
	 * Return the number of sub dungeons associated with this
	 * composed dungeon.
	 */
	@Basic @Raw
	public int getNbSubDungeons(){
		return subDungeons.size();
	}

	/**
	 * Check whether this composed dungeon can have the given dungeon
	 * as on of its sub dungeons.
	 * 
	 * @param 	dungeon
	 * 			The dungeon to check.
	 * @return	False if the given dungeon is not effective.
	 * 			| if(dungeon == null)
	 * 			| 	then result == false
	 * 			Otherwise, false if this composed dungeon is
	 * 			terminated or if the given dungeon is terminated.
	 * 			|else if(this.isTerminated()
	 * 			|		|| dungeon.isTerminated())
	 * 			|	then result == false
	 * 			Otherwise, false if the given dungeon is bigger
	 * 			than this composed dungeon.
	 * 			| else if (this.getMaxXCoordinate() < dungeon.getMaxXCoordinate()
	 * 			|		&& this.getMaxYCoordinate() < dungeon.getMaxYCoordinate()
	 * 			|		&& this.getMaxZCoordinate() < dungeon.getMaxZCoordinate())
	 * 			|	then result == false
	 * 			Otherwise, false if the given dungeon is a sub dungeon of
	 * 			an other dungeon or if the given dungeon has
	 * 			this dungeon as a sub dungeon, true otherwise.
	 * 			| else if (dungeon.getContainingDungeon() != this 
	 * 			|		|| (dungeon instanceof ComposedDungeon 
	 * 			|				&& ((ComposedDungeon) dungeon).hasAsSubDungeon(this)))
	 * 			|	then result == false
	 */
	@Raw
	public boolean canHaveAsSubDungeon(Dungeon<E> dungeon){
		if(dungeon == null)
			return false;
		if(this.isTerminated()|| dungeon.isTerminated())
			return false;
		if (this.getMaxXCoordinate() < dungeon.getMaxXCoordinate()
			 && this.getMaxYCoordinate() < dungeon.getMaxYCoordinate()
			 && this.getMaxZCoordinate() < dungeon.getMaxZCoordinate())
			 return false;
		if(dungeon.getContainingDungeon() != this 
				|| (dungeon instanceof ComposedDungeon 
						&& ((ComposedDungeon<E>) dungeon).hasAsSubDungeon(this)))
			return false;
		return true;
	}
	
	/**
	 * Check whether this composed dungeon can have the given dungeon 
	 * as one of its sub dungeons at the given position.
	 * 
	 * @param 	dungeon
	 * 			The dungeon to check
	 * @param 	position
	 * 			The position of the origin of the given dungeon in this composed dungeon.
	 * @return	False if this composed dungeon cannot have the given dungeon
	 * 			as a sub dungeon at any position.
	 * 			| if(!canHaveAsSubDungeon(dungeon))
	 * 			|	then result == false
	 * 			False if this dungeon cannot have the given position
	 * 			as a position.
	 * 			|if(!canHaveAsPosition(position)
	 * 			| 	then result == false
	 * 			False if the given dungeon would lay beyond the
	 * 			borders of this dungeon.
	 * 			|if(dungeon.getMaxXCoordinate()+position.getX() > this.getMaxXCoordinate()
	 *			|		&& dungeon.getMaxYCoordinate()+position.getY() > this.getMaxYCoordinate()
	 *			|		&& dungeon.getMaxZCoordinate()+position.getZ() > this.getMaxZCoordinate())
	 *			|	then result == false;
	 *			False if the given dungeon would overlap 
	 *			with an other dungeon, true otherwise.
	 *			|if(hasOverLappingDungeon(dungeon,position))
	 *			|	then result == false.
	 */
	public boolean canHaveAsSubDungeonAt(Dungeon<E> dungeon, Position position){
		if(! canHaveAsPosition(position))
			return false;
		if(!canHaveAsSubDungeon(dungeon))
			return false;
		if(dungeon.getMaxXCoordinate()+position.getX() > this.getMaxXCoordinate()
				&& dungeon.getMaxYCoordinate()+position.getY() > this.getMaxYCoordinate()
				&& dungeon.getMaxZCoordinate()+position.getZ() > this.getMaxZCoordinate())
			return false;
		if(hasOverLappingDungeon(dungeon,position))
			return false;
		return true;
	}
	
	/**
	 * Check whether this composed dungeon has proper sub
	 * dungeons associated with it.
	 * 
	 * @return	True if and only if this composed dungeon can have
	 * 			each of its sub dungeons as a sub dungeon at their
	 * 			position.
	 * 			| result ==
	 * 			|		for each dungeon in getAllSubDungeons()
	 * 			|			canHaveAsSubDungeonAt(dungeon, getSubDungeons().get(dungeon))
	 */
	public boolean hasProperSubDungeons(){
		for(Dungeon<E> dungeon: subDungeons.keySet())
			if(! canHaveAsSubDungeonAt(dungeon, subDungeons.get(dungeon)))
				return false;
		return true;
	}
	
	/**
	 * Check whether the given dungeon is one of the sub dungeons
	 * associated with this composed dungeon or with a sub dungeon
	 * of this dungeon.
	 * 
	 * @param 	dungeon
	 * 			The dungeon to be checked.
	 * @return	False if the given dungeon is not effective.
	 * 			|if(dungeon == null)
	 * 			|	then result == false
	 * @return	Otherwise, true if the given dungeon is a sub dungeon
	 * 			of this dungeon.
	 * 			|if(getAllSubDungeons.contains(dungeon))
	 * 			|	then result == true
	 * @return	Otherwise, true if a sub dungeon of this dungeon has
	 * 			the given dungeon as a sub dungeon.
	 * 			|for some subDungeon in getAllSubDungeons()
	 * 			|	if(	subDungeon.hasAsSubDungeon(dungeon))
	 * 			|		result == true
	 * @return	False otherwise. 
	 */
	@Basic @Raw
	public boolean hasAsSubDungeon(Dungeon<E> dungeon){
		if(dungeon == null)
			return false;
		for(Dungeon<E> subDungeon: getAllSubDungeons()){
			if(subDungeon == dungeon)
				return true;
			if(subDungeon instanceof ComposedDungeon 
					&& ((ComposedDungeon<E>) subDungeon).hasAsSubDungeon(dungeon))
				return true;
		}
		return false;
	}
	
	/**
	 * Set the sub dungeon attached to this composed dungeon to the
	 * given sub dungeon at the given position.
	 * 
	 * @param 	dungeon
	 * 			The sub dungeon to be attached to this composed dungeon
	 * @param 	position
	 * 			The position in this dungeon which will correspond to
	 * 			the origin of the given sub dungeon.
	 * @post	The given dungeon is attached to this composed dungeon as a subdungeon
	 * 			with its origin at the given position.
	 * 			|new.getSubDungeonAt(position) == dungeon
	 * 			|new.getPositionOfSubDungeon(dungeon) == position
	 * @throws	IllegalArgumentException
	 * 			This composed dungeon must be able to hold the given sub dungeon as a
	 * 			sub dungeon at the given position and the given sub dungeon may
	 * 			not already be attached to this composed dungeon and must be empty and the 
	 * 			given sub dungeon must already reference this composed dungeon as 
	 * 			its containing dungeon.
	 * 			| ! canHaveAsSubDungeonAt(subDungeon,position) || 0 != dungeon.getNbSquares()
	 * 			|		|| hasAsSubDungeon(subDungeon) 
	 * 			|		||subDungeon.getContainingDungeon()!= this
	 */
	public void addSubDungeonAt(Dungeon<E> dungeon, Position position){
		if(! canHaveAsSubDungeonAt(dungeon, position) || 0 != dungeon.getNbSquares() || hasAsSubDungeon(dungeon) || dungeon.getContainingDungeon()!= this)
			throw new IllegalArgumentException("Cannot place the given dungeon at the given position");
		this.subDungeons.put(dungeon,position);	
	}
	
	/**
	 * Remove the given dungeon from the set of sub
	 * dungeons attached to this composed dungeon.
	 * 
	 * @param 	dungeon
	 * 			The dungeon to be removed.
	 * @post	This composed dungeon does not have
	 * 			the given dungeon as one of its sub dungeons.
	 * 			| ! new.hasAsSubDungeon(dungeon)
	 * @post	If this composed dungeon has the given dungeon
	 * 			as one of its sub dungeons, the given
	 * 			dungeon no longer references a containing dungeon.
	 * 			| (new dungeon).getContainingDungeon() == null
	 * @throws	IllegalArgumentException
	 * 			The given dungeon must be terminated, otherwise, that dungeon
	 * 			cannot be removed.
	 * 			| ! dungeon.isTerminated()
	 */
	public void removeAsSubDungeon(Dungeon<E> dungeon){
		if(! dungeon.isTerminated())
			throw new IllegalArgumentException("Can only remove terminated sub dungeons.");
		subDungeons.remove(dungeon);
	}

	/**
	 * Check whether this composed dungeon has an overlapping
	 * dungeon with the given dungeon at the given position.
	 * 
	 * @param 	dungeon
	 * 			The dungeon that may not have any overlapping positions.
	 * @param 	position
	 * 			The position to check.
	 * @return 	False if for some position of the given dungeon in this composed
	 * 			dungeon, there's an other dungeon attached to that position,
	 * 			true otherwise.
	 * 			|for some (positionToCheck(x,y,z)| position.getX() < x < position.getX()+dungeon.getMaxXCoordinate()
	 * 											&& position.getY() < y < position.getY()+dungeon.getMaxYCooridinate()
	 * 											&& position.getZ() < z < position.getZ()+dungeon.getMaxZCoordinate()}
	 * 			|	if(getSubDungeonAt(positionToCheck) != null && getSubDungeonAt(positionToCheck)!= dungeon)
	 * 			|		result == false
	 * @throws 	IllegalArgumentException
	 * 			If the given dungeon is attached to this composed dungeon, it must be attached
	 * 			to this composed dungeon at the given position.
	 * 			|hasAsSubDungeon(dungeon) && getPositionOfSubDungeon(dungeon)!=position
	 */
	private boolean hasOverLappingDungeon(Dungeon<E> dungeon,Position position){
		if(hasAsSubDungeon(dungeon) && getPositionOfSubDungeon(dungeon)!=position)
			throw new IllegalArgumentException("The given subdungeon is a subdungeon of this dungeon, but the given position is not the position in this dungeon at which the origin of the given dungeon lies.");
		long x = 0; long y = 0; long z = 0;
		while(x<=dungeon.getMaxXCoordinate()&& y<=dungeon.getMaxYCoordinate() 
				&& z <=dungeon.getMaxZCoordinate()){
			Position positionToCheck = new Position(x,y,z).add(position);
			if(getSubDungeonAt(positionToCheck) != null && getSubDungeonAt(positionToCheck)!= dungeon)
				return true;
			if(x!=dungeon.getMaxXCoordinate())
					  x++;
			else if(y!=dungeon.getMaxYCoordinate()){
					  x=0; y++;}
			else{	  x=0; y=0; z++;}}
		return false;
	}

	/**
	 * Variable referencing a list of sub dungeons of this composed dungeon.
	 */
	private HashMap<Dungeon<E>,Position> subDungeons = new HashMap<Dungeon<E>,Position>();

	/**
	 * Return the square associated with this composed dungeon at the
	 * given position.
	 * 
	 * @return	The returned square is the square attached
	 * 			to the sub dungeon of this dungeon at the
	 * 			given position subtracted with the
	 * 			position of the origin of that dungeon.
	 * 			|result == 
	 * 			|	getSubDungeonAt(position).getSquareAt(position.subtract(getPositionOfSubDungeon(subDungeon)))
	 * @return	If the sub dungeon at the given position is non effective,
	 * 			then null is returned for the square at the given position.
	 * 			| if(getSubDungeonAt(position) == null)
	 * 			|	then result == null
	 */
	@Override
	@Basic 
	@Raw
	public E getSquareAt(Position position) throws IllegalArgumentException{
		if (! canHaveAsPosition(position))
			throw new IllegalArgumentException("Position is not valid.");
		Dungeon<E> subDungeon = getSubDungeonAt(position);
		if(subDungeon == null)
			return null;
		return subDungeon.getSquareAt(position.subtract(getPositionOfSubDungeon(subDungeon)));
	}

	/**
	 * Return the position at which the given square is
	 * registered as a square in this composed dungeon.
	 * 
	 * @return	The returned position is the position of the
	 * 			given square in the sub dungeon to which
	 * 			it is attached augmented with the position
	 * 			of the origin of that sub dungeon in this
	 * 			composed dungeon.
	 * 			|for some Dungeon subDungeon in getAllSubDungeons()
	 * 			|	if(subDungeon.hasAsSquare(square))
	 * 			|		result == subDungeon.getPositionOfSquare(square)add(getPositionOfSubDungeon(subDungeon))
	 */
	@Override
	@Raw
	public Position getPositionOfSquare(E square) throws IllegalArgumentException {	
		for(Dungeon<E> subDungeon: getAllSubDungeons())
			try{
				return subDungeon.getPositionOfSquare(square).add(getPositionOfSubDungeon(subDungeon));
			}
			catch(IllegalArgumentException e){
			}
		throw new IllegalArgumentException("The given square is not a square for this composed dungeon.");
	}

	/**
	 * Return the number of squares in this composed dungeon.
	 * 
	 * @return	Add all the numbers of squares of the
	 * 			sub dungeons.
	 * 			|result ==
	 * 			|	SUM(dungeon.getNbSquares()| dungeon in getAllSubDungeons())
	 */
	@Override
	@Basic
	public int getNbSquares() {
		int nbSquares= 0;
		for(Dungeon<E> subDungeon:getAllSubDungeons())
			nbSquares += subDungeon.getNbSquares();
		return nbSquares;
	}

	/**
	 * Check whether this composed dungeon has proper
	 * squares associated with it.
	 * 
	 * @return	False if a sub dungeon of this dungeon doesn't
	 * 			have proper squares attached to it, true otherwise.
	 * 			|for each subDungeon in getAllSubDungeons()
	 * 			|	if(! subDungeon.hasProperSquares())
	 * 			|		result == false
	 */
	@Override
	@Raw
	public boolean hasProperSquares() {
		for(Dungeon<E> subDungeon:getAllSubDungeons())
			if(!subDungeon.hasProperSquares())
				return false;
		return true;
	}

	/**
	 * Check whether there is a square at the given position in this composed dungeon.
	 * 
	 * @return	False if the given position does not belong 
	 * 			to any sub dungeon of this composed dungeon.
	 * 			| getSubDungeonAt(position) == null
	 * @return	True if and only if the sub dungeon to which
	 * 			the given position belongs has a square at the
	 * 			given position subtracted with the position of
	 * 			the origin of that sub dungeon in this composed dungeon.
	 * 			| result == 
	 * 			|		subDungeon.hasSquareAt(position.subtract(getPositionOfSubDungeon(subDungeon)))
	 */
	@Override
	@Basic
	public boolean hasSquareAt(Position position){
		Dungeon<E> subDungeon = getSubDungeonAt(position);
		if(subDungeon == null)
			return false;
		return subDungeon.hasSquareAt(position.subtract(getPositionOfSubDungeon(subDungeon)));
	}

	/**
	 * Check whether the given square is attached to this  composed dungeon.
	 * 
	 * @return	True if and only if the given square belongs to a sub dungeon
	 * 			of this composed dungeon, false otherwise.
	 * 			|for some subDungeon in getAllSubDungeons()
	 * 			|	if (subDungeon.hasAsSquare(square))
	 * 			|		result == true
	 */
	@Override
	@Raw
	public boolean hasAsSquare(Square square) {
		for(Dungeon<E> subDungeon : getAllSubDungeons())
			if(subDungeon.hasAsSquare(square))
				return true;
		return false;
	}
	
	/**
	 * Check whether this composed dungeon can have the given square at
	 * the given position.
	 * 
	 * @return 	False if this composed dungeon does not satisfy the method defined at the level of the superclass.
	 * 			| if(! super.canHaveAsSquareAt(square, position))
	 * 			|	then result == false
	 * @return	False if the sub dungeon at the given position is non-effective 
	 * 			or if the sub dungeon at the given position cannot have the given position 
	 * 			subtracted with the position of the origin of the sub dungeon in this dungeon
	 * 			as a position for a square. True otherwise.
	 * 			| if(getSubDungeonAt(position) == null 
	 * 			|	|| ! getSubDungeonAt(position).canHaveAsPosition(position.subtract(getPositionOfSubDungeon(getSubDungeonAt(position)))))
	 * 			|		then result == false
	 */
	@Override
	public boolean canHaveAsSquareAt(E square, Position position){
		if(! super.canHaveAsSquareAt(square, position))
			return false;
		if(getSubDungeonAt(position) == null || ! getSubDungeonAt(position).canHaveAsPosition(position.subtract(getPositionOfSubDungeon(getSubDungeonAt(position)))))
			return false;
		return true;
	
	}

	/**
	 * Return the number of slippery squares in this composed
	 * dungeon.
	 * 
	 * @return	Add all the numbers of slippery squares of the
	 * 			sub dungeons.
	 * 			|result ==
	 * 			|	SUM(dungeon.getNbSlipperySquares()|dungeon in getAllSubDungeons())
	 */
	@Override
	@Basic
	@Model
	protected int getNbSlipperySquares() {
		int nbSlipperySquares= 0;
		for(Dungeon<E> subDungeon:getAllSubDungeons())
			nbSlipperySquares += subDungeon.getNbSlipperySquares();
		return nbSlipperySquares;
	}

	/**
	 * Add the given square at the given position as a square for this composed dungeon.
	 * 
	 * @effect	The given square will be added at the given position, 
	 * 			to the sub dungeon that contains the given position.
	 * 			| getSubDungeonAt(position).addSquare(position.subtract(getPositionOfSubDungeon(getSubDungeonAt(position))), square)
	 * @effect	A border will be constructed at the given square 
	 * 			at the given position in the sub dungeon.
	 * 			| borderConstructionWithOtherDungeon(square,position,getSubDungeonAt(position))
	 * @effect	All rocks surrounding the space to which the newly added
	 * 			square belongs will recalculate their temperature.
	 * 			|for rock in new.getSurroundingRocks(square)
	 * 			|		rock.setTemperature();
	 * @throws 	IllegalArgumentException
	 * 			It's impossible to add a square at a position that doesn't belong to a sub dungeon of this composed dungeon.
	 * 			| getSubDungeonAt(position) == null
	 */
	@Override
	public void addSquare(Position position, E square)
			throws IllegalArgumentException {
		Dungeon<E> subDungeon = getSubDungeonAt(position);
		if(subDungeon == null)
			throw new IllegalArgumentException("It's impossible to add a square at a position that doesn't belong to a sub dungeon of this composed dungeon.");
		subDungeon.addSquare(position.subtract(getPositionOfSubDungeon(subDungeon)), square);
		borderConstructionWithOtherDungeon(square,position,subDungeon);
		if(!(square instanceof Rock)){
			HashSet<Rock> surroundingRocks = getSurroundingRocks(square);
			for(Rock rock : surroundingRocks)
				rock.setTemperature();
		}
	}

	/**
	 * Remove the given square from the collection of squares 
	 * attached to this composed dungeon.
	 * 
	 * @effect	The given square is removed from the sub dungeon that
	 * 			is associated with the given square.
	 * 			| if(hasAsSquare(square))
	 * 			|	getSubDungeonAt(getPositionOfSquare(square)).removeSquare(square)
	 */
	@Override
	public void removeSquare(E square) {
		if(hasAsSquare(square)){
			Dungeon<E> subDungeon = getSubDungeonAt(getPositionOfSquare(square));
			subDungeon.removeSquare(square);
		}
	}

	/**
	 * Remove the square at the given position in this composed dungeon.
	 * 
	 * @effect	The square is removed from the dungeon that
	 * 			is associated with this composed dungeon
	 * 			at the given position.
	 * 			| getSubDungeonAt(position).removeSquareAt(position.subtract(getPositionOfSubDungeon(getSubDungeonAt(position))))			
	 */
	@Override
	public void removeSquareAt(Position position) {
		Dungeon<E> subDungeon = getSubDungeonAt(position);
		subDungeon.removeSquareAt(position.subtract(getPositionOfSubDungeon(subDungeon)));
		
	}

	/**
	 * Return a set of all Levels and Shafts that
	 * are associated with this composed dungeon or
	 * with a sub dungeon of this composed dungeon.
	 * 
	 * @return	The returned set is effective.
	 * 			| result != null
	 * @return	Each dungeon in the result set is a sub dungeon
	 * 			of this dungeon or of one of its sub dungeons.
	 * 			|for all leafDungeon in result
	 * 			|	hasAsSubDungeon(leafDungeon)
	 * @return	Each sub dungeon of this dungeon that is a leafDungeon
	 * 			is returned in the set.
	 * 			|for each subDungeon in getAllSubDungeons()
	 * 			|	result.contains(subDungeon) 
	 * 			|	|| if(! subDungeon instanceof LeafDungeon)
	 * 			|		 for each subDungeon2 in subDungeon.getAllLeafDungeons()
	 * 			|			result.contains(subDungeon2)
	 */
	public HashSet<LeafDungeon<E>> getAllLeafDungeons(){
		HashSet<LeafDungeon<E>> result = new HashSet<LeafDungeon<E>>();
		for(Dungeon<E> subDungeon: getAllSubDungeons()){
			if(subDungeon instanceof LeafDungeon)
				result.add((LeafDungeon<E>) subDungeon);
			else{
				result.addAll(((ComposedDungeon<E>) subDungeon).getAllLeafDungeons());
			}
		}
		return result;
	}

	/**
	 * Check whether this composed dungeon can have the given composed
	 * dungeon as its containing dungeon.
	 * 
	 * @return	If this method defined at the
	 * 			level of the superclass returns false,
	 * 			the result will also be false;
	 * 			|if(!super.canHaveAsContainingDungeon(dungeon))
	 * 			|	then result == false;
	 * @return	True if this dungeon hasn't got
	 * 			the given dungeon as a sub dungeon.
	 * 			| result == ! this.hasAsSubDungeon(dungeon)
	 */
	@Override
	public boolean canHaveAsContainingDungeon(ComposedDungeon<E> dungeon){
		if(!super.canHaveAsContainingDungeon(dungeon))
			return false;
		return ! this.hasAsSubDungeon(dungeon);
	}
	
	/**
	 * Construct a border with a square from an other sub dungeon than
	 * the sub dungeon to which the given square belongs or merge the
	 * spaces that were separated if they are now connected through
	 * the given square.
	 * 
	 * @param square
	 * 			The square that lies on the edge of a dungeon.
	 * @param position
	 * 			The position of the square in this dungeon.
	 * @param subDungeon
	 * 			The sub dungeon of this dungeon in which the new square was added.
	 * 			
	 * @effect	If this square lies on the edge of the given sub dungeon
	 * 			and 
	 * 			if there is a square at a neighboring position in this
	 * 			dungeon that doesn't belong to the sub dungeon of the square
	 * 			and at least one of the squares has a border at the common borderline,
	 * 			the most closed border will be kept.
	 * 			If this border is an open door, and if the squares aren't connected
	 * 			in any other way, or if there's no border between the 2 squares,
	 * 			the 2 spaces on each side of the door will be merged.
	 * 			
	 * 			|for(Direction direction: Direction.values()){
	 *			|	if((subDungeon).getAllSquaresAtBorder(direction).containsKey(square) 
	 *			|			&& canHaveAsPosition(direction.goStepInDirection(position))){
	 *			|		E neighbor = getSquareAt(direction.goStepInDirection(position));
	 *			|	if(neighbor != null && ((square.getBorder(direction)!=null && square.getBorder(direction).getOtherSquare(square) == null)
	 *			|			|| (neighbor.getBorder(direction.getOpposite())!= null && neighbor.getBorder(direction.getOpposite()).getOtherSquare(neighbor) == null))){
	 *			|		borderConstruction(square, neighbor, direction);}
	 *			|		if (square.getBorder(direction) instanceof Door && !((Door) square.getBorder(direction)).isClosed() 
	 *			|				&& ! newSpace.contains(neighbor))
	 *			|			newSpace = mergeSpaces(newSpace, getSpace(neighbor));
	 *			|	else if (square.getBorder(direction)==null && neighbor != null && ! newSpace.contains(neighbor)){
	 *			|		newSpace = mergeSpaces(newSpace,getSpace(neighbor));
	 *			|	}}}
	 *			
	 */
	private void borderConstructionWithOtherDungeon(E square, Position position, Dungeon<E> subDungeon){
		HashSet<TeleportationSquare> newSpace = new HashSet<TeleportationSquare>();
		if(!(square instanceof Rock))
			newSpace.addAll(subDungeon.getSpace(square));
		for(Direction direction: Direction.values()){
			if((subDungeon).getAllSquaresAtBorder(direction).containsKey(square) 
					&& canHaveAsPosition(direction.goStepInDirection(position))){
				E neighbor = getSquareAt(direction.goStepInDirection(position));
				if(neighbor != null && ((square.getBorder(direction)!=null && square.getBorder(direction).getOtherSquare(square) == null)
						|| (neighbor.getBorder(direction.getOpposite())!= null && neighbor.getBorder(direction.getOpposite()).getOtherSquare(neighbor) == null))){
					borderConstruction(square, neighbor, direction);
					if (square.getBorder(direction) instanceof Door && !((Door) square.getBorder(direction)).isClosed() 
							&& ! newSpace.contains(neighbor))
						newSpace = mergeSpaces(newSpace, getSpace(neighbor));
				}
				else if (square.getBorder(direction)==null && neighbor != null && ! newSpace.contains(neighbor)){
					newSpace = mergeSpaces(newSpace,getSpace(neighbor));
				}
			}
		}
	}

}
